package com.gitee.wsl.mathematics.coordinate.ext

import com.gitee.wsl.mathematics.coordinate.d3.Coordinate3
import com.gitee.wsl.mathematics.matrix.mat4.Matrix4
import com.gitee.wsl.mathematics.matrix.mat4.ext.times
import com.gitee.wsl.mathematics.vector.vec3.Vector3
import com.gitee.wsl.mathematics.vector.vec4.Vector4

internal val <T:Number,V:Coordinate3<T, V>> Coordinate3<T, V>.xyz0 get() = vec4
internal val <T:Number,V:Coordinate3<T, V>> Coordinate3<T, V>.xyz1 get() = vec4.create(x, y, z, one) as Vector4<T,*>

/**
 * Project a 3D point on a 2D surface
 *
 * [point] the point to project
 * [projection] the projection matrix
 * [view] the view matrix
 * [width] the width of the projection surface
 * [height] the height of the projection surface
 */
fun<T:Number,V:Coordinate3<T, V>> project(point: Coordinate3<T, V>, projection: Matrix4<T,*,*>, view: Matrix4<T,*,*>, width: Int, height: Int): Coordinate3<T, *> {

    val projected = ((projection * view) * point.xyz1).div

    return point.run {
        create(
            (projected.x  + 1) * width / 2,
            (1 - projected.y ) * height / 2,
            (projected.z + 1) / 2
        )
    }
}

fun<T:Number,V:Coordinate3<T, V>> unproject(point: Coordinate3<T, V>, projection: Matrix4<T,*,*>, view: Matrix4<T,*,*>, width: Int, height: Int): Vector3<T, *> {
    val ipm = (projection * view).inversed as Matrix4<T,*,*>
    point.run {
      val v =  create(2 * point.x / width - 1, 2 * point.y / height - 1, 2 * point.z - 1)
      return (ipm * v.xyz1).div
    }
}

//fun<T:Number,V:Coordinate3<T, V>>  Coordinate3<T, V>.project(point: Coordinate3<T, V>, projection: Matrix44, view: Matrix44, width: Int, height: Int)